import base64
import pickle

from typing import Any, List

from django_redis import get_redis_connection
from ninja.responses import Response
from ninja_extra import api_controller, route
from ninja_jwt.authentication import JWTAuth

from goods.models import SKU
from meiduo_mall.base.schemas import Message
from .schemas import CartSchemaIn, CartSKUSchema, CartSelectSchema


@api_controller(tags=['个人中心'])
class CartsAPI:
    @route.post('carts/', summary='添加购物车记录', response={200: Any, 400: Message})
    def post(self, data: CartSchemaIn):
        try:
            user = JWTAuth().jwt_authenticate(self.context.request,
                                              self.context.request.headers['Authorization'].split(' ')[-1])
        except:
            user = None

        if user:
            """登录用户"""
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            """
            redis.exceptions.DataError: Invalid input of type: 'dict'. Convert to a bytes, string, int or float first.
            想尝往redis里面的hash存字典套字典，报错了，只能存字节，字符，整型和浮点            
            """
            pl.hincrby(f'cart_{user.id}', data.sku_id, data.count)
            if data.selected:
                pl.sadd(f'selected_{user.id}', data.sku_id)
            pl.execute()
            return data

        else:
            """未登录用户"""
            """
            {
            'sku_id_1':{'count':1,'selected':True}
            'sku_id_16':{'count':1,'selected':True}
            }
            """
            # 获取cookie中购物车的数据
            cart_str = self.context.request.COOKIES.get('cart')
            if cart_str:  # 购物车中有商品
                # 字符串转bytes类型的字符串
                cart_str_bytes = cart_str.encode()
                # bytes转换成bytes
                cart_bytes = base64.b64decode(cart_str_bytes)
                # bytes转成字典
                cart_dict = pickle.loads(cart_bytes)
            else:
                cart_dict = {}

            # 增量计算
            if data.sku_id in cart_dict:
                # 判断商品是否已存在购物车字典中
                origin_count = cart_dict[data.sku_id]['count']
                data.count += origin_count
            # 新的商品添加到购物车中
            cart_dict[data.sku_id] = {
                'count': data.count,
                'selected': data.selected,
            }
            # 将字典转换成bytes类型
            cart_bytes = pickle.dumps(cart_dict)
            # 将bytes转成bytes类型的字符串
            cart_str_bytes = base64.b64encode(cart_bytes)
            # 将bytes类型的字符串转成字符串
            cart_str = cart_str_bytes.decode()
            response = Response(data, status=200)
            response.set_cookie('cart', cart_str)
            return response

    @route.get('carts/', summary='查询购物车记录', response={200: List[CartSKUSchema], 400: Message})
    def get(self):
        try:
            user = JWTAuth().jwt_authenticate(self.context.request,
                                              self.context.request.headers['Authorization'].split(' ')[-1])
        except:
            user = None
        # 格式化redis和cookie中的购物车数据
        if user:
            """登录用户"""
            # 连接redis
            redis_conn = get_redis_connection('cart')
            # 获取hash数据
            cart_redis_dict = redis_conn.hgetall(f'cart_{user.id}')
            # 获取set集合数据
            selecteds = redis_conn.smembers(f'selected_{user.id}')
            # 格式化redis中的购物车数据
            cart_dict = {}
            for sku_id_bytes, count_bytes in cart_redis_dict.items():
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(count_bytes),
                    'selected': sku_id_bytes in selecteds,
                }
        else:
            """未登录用户"""
            cart_str = self.context.request.COOKIES.get('cart')
            if cart_str:
                # 将cookies中的字符串转成字典
                cart_str_bytes = cart_str.encode()
                cart_bytes = base64.b64decode(cart_str_bytes)
                cart_dict = pickle.loads(cart_bytes)
            else:
                return 400, {'msg': '购物车为空'}
        # 将格式化后的数据返回给前端
        # 根据sku_id查询sku模型
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']
        return skus

    @route.put('carts/', summary='更新购物车记录', response={200: Any, 400: Message})
    def put(self, data: CartSchemaIn):
        try:
            user = JWTAuth().jwt_authenticate(self.context.request,
                                              self.context.request.headers['Authorization'].split(' ')[-1])
        except:
            user = None

        if user:
            """登录用户"""
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 修改商品数量为新值
            pl.hset(f'cart_{user.id}', data.sku_id, data.count)
            # 勾选商品的更新
            if data.selected:
                pl.sadd(f'selected_{user.id}', data.sku_id)
            else:
                # 未勾选商品从集合中移除
                pl.srem(f'selected_{user.id}', data.sku_id)
            pl.execute()
            return data
        else:
            """未登录用户"""
            cart_str = self.context.request.COOKIES.get('cart')
            if cart_str:
                # 将cookies中的字符串转成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return 400, {'msg': '数据获取失败'}
            cart_dict[data.sku_id] = {
                'count': data.count,
                'selected': data.selected,
            }
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response = Response(data, status=200)
            response.set_cookie('cart', cart_str)
            return response

    @route.delete('carts/', summary='删除购物车记录', response={200: Any, 400: Message})
    def delete(self, data: CartSchemaIn):
        try:
            user = JWTAuth().jwt_authenticate(self.context.request,
                                              self.context.request.headers['Authorization'].split(' ')[-1])
        except:
            user = None

        if user:
            """登录用户"""
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 删除hash数据
            pl.hdel(f'cart_{user.id}', data.sku_id)
            # 删除set数据
            pl.srem(f'selected_{user.id}', data.sku_id)
            pl.execute()
        else:
            """未登录用户"""
            response = Response(None, status=200)
            cart_str = self.context.request.COOKIES.get('cart')
            # 获取cookie购物车
            if cart_str:
                # cookie字符串转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return 400, {'msg': '购物车为空'}
            # 从cookie中删除sku商品
            if data.sku_id in cart_dict:
                del cart_dict[data.sku_id]
            # 如果购物车还有商品
            if len(cart_dict.keys()):
                cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('cart', cart_str)
            else:
                # 购物车已删除空
                response.delete_cookie('cart')
            return response


@api_controller(tags=['个人中心'])
class CartSelectionAPI:
    @route.put('carts/selection/', summary='购物车全选', response={200: Any})
    def select_all(self, data: CartSelectSchema):
        try:
            user = JWTAuth().jwt_authenticate(self.context.request,
                                              self.context.request.headers['Authorization'].split(' ')[-1])
        except:
            user = None
        if user:
            """登录用户"""
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            cart_redis_dict = pl.hgetall(f'cart_{user.id}')
            sku_ids = cart_redis_dict.keys()
            if data.selected:
                # 全选
                pl.sadd(f'selected_{user.id}', *sku_ids)
            else:
                # 全不选
                pl.srem(f'selected_{user.id}', *sku_ids)
            pl.execute()

        else:
            """未登录用户"""
            response = Response(None, status=200)
            cart_str = self.context.request.COOKIES.get('cart')
            # 获取cookie购物车
            if cart_str:
                # cookie字符串转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return 200, {'msg': '购物车为空'}
            # 将购物车中数据全选/全不选
            for sku_id in cart_dict.keys():
                cart_dict[sku_id]['selected'] = data.selected
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart', cart_str)
            return response
